﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace Autorun
{
	[Serializable()]
	public sealed class IOAccessManagement : MarshalByRefObject
	{
		#region "Singleton Design-Pattern"
		private static IOAccessManagement _instance = new IOAccessManagement();

		public static IOAccessManagement Instance
		{
			get { return _instance; }
		}
		#endregion

		#region "Local FileSystem Access"
		public DriveInfo[] GetDrives()
		{
			DriveInfo[] rc;

			rc = DriveInfo.GetDrives();
			return rc;
		}

		public string[] GetDirectories(string path)
		{
			string[] rc;

			try
			{
				rc = Directory.GetDirectories(path);
			}
			catch (Exception ex)
			{
#if DEBUG
				System.Windows.Forms.MessageBox.Show(string.Format("Error in GetDirectories('{0}')\n\n{1}", path, ex.ToString()), System.Windows.Forms.Application.ProductName, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning);
#endif
				rc = null;
			}

			return rc;
		}

		public SortedList<string, string> GetFiles(string path)
		{
			SortedList<string, string> rc;
			string[] files;
			Assembly assembly;
			Module[] modules;
			Type[] types;

			rc = new SortedList<string, string>();

			try
			{
				files = Directory.GetFiles(path);
				foreach (string file in files)
					/// TODO: Important note about the condition on "Autorun.Framework.dll"
					/// When scanning files, the assembly are loaded into memory. If you load
					/// a file Autorun.Framework.dll before THE library provided with Autorun.exe
					/// you will not be able to cast task classes into TaskBase objects
					if ((Path.GetExtension(file).ToLower() == ".dll" || Path.GetExtension(file).ToLower() == ".exe") && (Path.GetFileName(file).ToLower() != "autorun.framework.dll"))
					{
						try
						{
							assembly = Assembly.LoadFrom(file);
							modules = assembly.GetModules();
						}
						catch (Exception ex)
						{
							LogManagement.Instance.AddEntry(SeverityLevels.Error, string.Format("Error in IOAccessManagement::GetFiles({0})", path), ex);
							modules = null;
						}
						if (modules != null)
							foreach (Module module in modules)
							{
								types = module.GetTypes();
								foreach (Type type in types)
									try
									{
										/// TODO: Find out why the following line seems to instanciate a wrong TaskBase data type
										//if (type.BaseType.ToString() == typeof(TaskBase).ToString())	
										if (type.IsSubclassOf(typeof(TaskBase)))
											rc.Add(type.ToString(), module.ToString());
									}
									finally { }
							}
					}
			}
			catch (Exception ex)
			{
				LogManagement.Instance.AddEntry(SeverityLevels.Error, string.Format("Error in IOAccessManagement::GetFiles({0})", path), ex);
			}

			return rc;
		}
		#endregion

		#region "Task Loader"
		internal TaskBase LoadTask(string fileName, string assemblyName, string className)
		{
			TaskBase rc;
			Assembly asm;
			object o;

			if (File.Exists(fileName) == false)
			{
				LogManagement.Instance.AddEntry(SeverityLevels.Error, string.Format("The specified library cannot be found:\n[{0}]", fileName), null);
				rc = null;
			}
			else
			{
				asm = Assembly.LoadFrom(fileName);
				// Can be caused when a parameterized constructor is defined but the default constructor has not been declared
				try
				{
					o = asm.CreateInstance(className);
					rc = (TaskBase)o;

					if (rc == null)
						throw new Exception(String.Format("The specified class couldn't be found:\n[{0}]", className));

				}
				catch (Exception ex)
				{
					LogManagement.Instance.AddEntry(SeverityLevels.Error, string.Format("Error while trying to load task (filename: \"{0}\", assembly name: \"{1}\", class name: \"{2}\")", fileName, assemblyName, className), ex);
					rc = null;
				}
			}
			return rc;
		}
		#endregion
	}
}
